package com.apobates.jforum.grief.aurl.utils;

import com.apobates.jforum.grief.aurl.spring.StringUtils;
import com.apobates.jforum.grief.aurl.utils.defend.Assertion;
import com.apobates.jforum.grief.aurl.utils.defend.PredicateQueue;
import com.apobates.jforum.grief.aurl.utils.defend.DefendAssert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 花括号作为占位符的工具类
 */
public final class CurlyBraceSymbol {
    private final String value;
    private static final Pattern PATH_VARI = Pattern.compile("\\{(.*?)\\}");
    private static final Logger logger = LoggerFactory.getLogger(CurlyBraceSymbol.class);
    /**
     * 初始化
     * @param value 带处理内容
     */
    public CurlyBraceSymbol(String value) {
        this.value = value;
    }

    /**
     * 使用默认值初始化
     * @return
     */
    public static CurlyBraceSymbol empty(){
        return new CurlyBraceSymbol("");
    }

    /**
     * 替换掉所有占位符的值
     * 示例:
     *   new CurlyBraceSymbol("/api/employees/{id}/{action}").replace(Map.of("id", "1", "action", "revoke"))
     *   返回: "/api/employees/1/revoke"
     * @param paramValueMap key=路径中变量的占位符名称, value=占位符的替换值
     * @return
     */
    public String replace(Map<String,String> paramValueMap){
        String local = this.expected(PredicateQueue.require().notEmpty().asserts(Assertion.single(paramValueMap)));
        for (Map.Entry<String,String> entry: paramValueMap.entrySet()) {
            if(null == entry.getKey()){
                continue;
            }
            local = local.replace(String.format("{%s}", entry.getKey()), entry.getValue());
        }
        return local;
    }

    /**
     * 替换掉所有占位符的值
     * 示例:
     *    new CurlyBraceSymbol("/api/employees/{id}/{action}").replace(List.of(Pair.of("id", "1"), Pair.of("action", "revoke")))
     *    返回: "/api/employees/1/revoke"
     * @param paramValuePairSet left=占位符名称, right=占位符的替换值
     * @return
     */
    public String replace(Collection<Pair<String,String>> paramValuePairSet){
        String local = this.expected(PredicateQueue.require().notEmpty().asserts(Assertion.single(paramValuePairSet))); //(DefendAssert.single(paramValuePairSet).notNull().notEmpty());
        for (Pair<String,String> pair: paramValuePairSet) {
            local = local.replace(String.format("{%s}", pair.left()), pair.right());
        }
        return local;
    }

    /**
     * 替换掉所有占位符的值
     * 示例:
     *     new CurlyBraceSymbol("/api/employees/{id}/{action}").replace(new TBean(1, "revoke"))
     *     返回: "/api/employees/1/revoke"
     * @param instance 对象实例. 占位符可以通过getter方法取得值
     * @return
     * @param <T>
     */
    public <T> String replace(T instance)  {
        String local = this.expected(PredicateQueue.require().asserts(Assertion.single(instance))); //(DefendAssert.single(instance).notNull());
        PropertyDescriptor pd;
        Method getMethod;
        Iterator<String> temp = getNames().iterator();
        while (temp.hasNext()){
            String attr = temp.next();
            try {
                pd = new PropertyDescriptor(attr, instance.getClass());
                getMethod = pd.getReadMethod();// 获得get方法
                Object value = getMethod.invoke(instance);
                if(null == value){
                    continue;
                }
                //
                local = local.replace(String.format("{%s}", attr), value.toString());
            }catch (InvocationTargetException|IllegalAccessException|IntrospectionException e){
                logger.debug("[CBS]replace has exception:"+e.getMessage());
            }
        }
        return local;
    }

    private <T> String expected(DefendAssert<T> other){
        // DefendAssert.single(this.value).define((s)->StringUtils.hasLength(s), "CurlyBraceSymbol处理的目标不可用").before(other).suspend();
        PredicateQueue.<String>predicate((s)->StringUtils.hasLength(s), "CurlyBraceSymbol处理的目标不可用")
                .asserts(Assertion.single(this.value))
                .before(other)
                .suspend();
        return this.value;
    }

    /**
     * 返回所有占位符
     * 示例:
     *    new CurlyBraceSymbol("/api/employees/{id}/{action}").getSymbol()
     *    返回: Set.of("{id}", "{action}")
     * @return 集合中的元素包含左右花括号, 例: {xx}
     */
    public Set<String> getSymbol(){
        if(isEmpty()){
            return Collections.emptySet();
        }
        Matcher matcher = PATH_VARI.matcher(this.value);
        Set<String> data = new HashSet<>();
        while (matcher.find()) {
            data.add(matcher.group());
        }
        return data;
    }

    /**
     * 返回所有占位符
     * 示例:
     *    new CurlyBraceSymbol("/api/employees/{id}/{action}").getNames()
     *    返回: Collection.of("id", "action")
     * @return 迭代中的元素不包含左右花括号, 例: xx
     */
    public Iterable<String> getNames(){
        // return StreamSupport.stream(getSymbol().spliterator(), false).map(ele->ele.substring(1, ele.length() -1).trim()).collect(Collectors.toList());
        return getSymbol().stream().map(ele->ele.substring(1, ele.length() -1).trim()).collect(Collectors.toList());
    }

    /**
     * 是否存在占位符.
     * 规则: 从头开始找字符. 找到第一对{}即判定存在占位符。
     * 适用: 目标很小情况下
     * 示例:
     *    new CurlyBraceSymbol("/api/employees/{id}/{action}").hasSymbol()
     *    返回: true
     *
     *    new CurlyBraceSymbol("/api/employees/1/revoke").hasSymbol()
     *    返回: false
     * @return 存在返回true
     */
    public boolean hasSymbol(){
        char[] temp = this.expected(DefendAssert.empty()).toCharArray();
        //
        int firstStartSymbol = -1;
        int firstEndSymbol = -1;
        // 找到第一对停止
        for (int i = 0; i<temp.length; i++){
            if(firstStartSymbol == -1 && temp[i]=='{'){
                firstStartSymbol = i;
            }
            if(firstEndSymbol == -1 && temp[i]=='}'){
                firstEndSymbol = i;
            }
            if(firstStartSymbol > -1 && firstEndSymbol > -1){
                break;
            }
        }
        logger.debug(String.format("[CBS]target: %s, first symbol sindex: %d, first symbol eindex: %d", value, firstStartSymbol, firstEndSymbol));
        return firstStartSymbol > 0 && firstEndSymbol > firstStartSymbol;
    }

    /**
     * 带处理内容是否是空的
     * @return
     */
    public boolean isEmpty(){
        return !StringUtils.hasLength(this.value);
    }
}
